#pragma once

#include <iostream>
#include <cstdio>
#include <vector>
#include <semaphore.h>
#include <cassert>

#define MAXSIZE 5

template <class T>
class RingQueue
{
public:
    RingQueue()
    {}

    RingQueue(const size_t _cap = MAXSIZE)
        : _queue(_cap), _capacity(_cap),_proIndex(0),_conIndex(0)
    {
        int n = sem_init(&_producer, 0, _cap);
        assert(n == 0);
        n = sem_init(&_consumer, 0, 0);
        assert(n == 0);
        (void)n;  // 这里的意思是防止编译器因没有使用定义了的参数n而警告

        pthread_mutex_init(&_proLock,nullptr);
        pthread_mutex_init(&_conLock,nullptr);

    }

    void push(const T& data)
    { 
        //1.申请producer的信号量
        sem_wait(&_producer);
        // 申请成功则放入数据
        // 申请成功就可以拿锁进入生产了

        // 加锁对这里形成临界区，该位置不能同时被多个线程访问
        pthread_mutex_lock(&_proLock);
        printf("thread[0x%x]进行生产\n",pthread_self());
        _queue[_proIndex++] = data; 
        _proIndex %= _capacity;  // 用取模来模拟环
        pthread_mutex_unlock(&_proLock);
 
        sem_post(&_consumer);   // 已经push了一个数据，说明消费者可以消费的资源多了一个即，消费者的信号量++

    }

    void pop(T* data)
    {
        //1. 申请consumer的信号量
        sem_wait(&_consumer);
        
        pthread_mutex_lock(&_conLock);
        printf("thread[0x%x]进行消费\n",pthread_self());
        *data =  _queue[_conIndex++];
        _conIndex %= _capacity;
        pthread_mutex_unlock(&_conLock);

        sem_post(&_producer);

    }

    ~RingQueue()
    {
        sem_destroy(&_producer);
        sem_destroy(&_consumer);
    }

private:
    std::vector<T> _queue;
    size_t _capacity;
    size_t _proIndex;
    size_t _conIndex;
    sem_t _producer;
    sem_t _consumer;
    pthread_mutex_t _proLock;
    pthread_mutex_t _conLock;
};